﻿/*************************************************************************************

- CLR版本：        4.0.30319.42000
- 类 名 称：       SocketHelper
- 机器名称：       YTE
- 命名空间：       LD.Common
- 文 件 名：       SocketHelper
- 创建时间：       2022/7/27 10:01:36
- 作    者：       YTE
- 说   明：
- 修改时间：
- 修 改 人：
  *************************************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LD.Host
{
    public class SocketHelper : ICommunicate
    {
        private Socket m_socket;
        public event Action<byte[]> ReceiveDataEvent;
        public event ConnectionStatusDelegate StatusEvent;
        private ManualResetEvent m_connectTime = new ManualResetEvent(false);
        private ManualResetEvent m_receiveTime = new ManualResetEvent(false);
        private IPEndPoint m_ipEndPoint;
        private Encoding m_encoding;
        private object m_objConnect = new object();
        private Status status = Status.失败;
        private object obj = new object();
        public string Name { get; set; }
        public Status Status
        {
            get { return status; }
            set
            {
                status = value;
                //StatusEvent?.BeginInvoke(Name, value, null, m_socket);
                StatusEvent?.Invoke(Name, value);
            }
        }

        public SocketHelper(string ip, int port, Encoding encoding)
        {
            m_ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            m_encoding = encoding;
        }
        public SocketHelper(string ip, int port) : this(ip, port, Encoding.UTF8)
        {

        }
        private void SetXintiao()
        {
            //byte[] inValue = new byte[] { 1, 0, 0, 0, 0x20, 0x4e, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间20 秒, 间隔侦测时间2 秒
            byte[] inValue = new byte[] { 1, 0, 0, 0, 0x88, 0x13, 0, 0, 0xd0, 0x07, 0, 0 };// 首次探测时间5 秒, 间隔侦测时间2 秒
            m_socket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
        }

        /// <summary>
        /// 重新连接
        /// </summary>
        private void Reconnect()
        {
            try
            {
                Task.Delay(100).Wait();
                Console.WriteLine("重连中。。。");
                Status = Status.失败;
                m_socket?.Close();
                Start();
            }
            catch (Exception)
            {
            }
        }
        /// <summary>
        /// 连接回调
        /// </summary>
        /// <param name="ar"></param>
        private void ConnectionCallBack(IAsyncResult ar)
        {
            lock (m_objConnect)
            {
                Socket socket = ar.AsyncState as Socket;
                if (socket != null && socket.Connected)
                {
                    try
                    {
                        socket.EndConnect(ar);
                        Status = Status.成功;
                        StartKeepAlive();
                    }
                    catch (Exception)
                    {
                        //LogHelper.Log($"【SocketHelper.CallBack】" + ex.Message, LogLevel.严重);
                    }
                    finally
                    {
                    }
                }
                else
                {
                    Reconnect();
                }
                m_connectTime.Set();
            }
        }
        public void Send(string sendMessage)
        {
            ReceiveString = string.Empty;
            if (sendMessage == null || sendMessage.Length == 0)
                return;
            if (m_socket.Connected)
            {
                try
                {
                    byte[] bs = m_encoding.GetBytes(sendMessage);
                    int flag = m_socket.Send(bs);
                }
                catch (Exception)
                {
                    Status = Status.失败;
                    //LogHelper.Log($"【SocketHelper.Send】" + ex.Message, LogLevel.严重);
                }
            }
        }
        public byte[] Send(byte[] sendMessage, int timeOut = 1000)
        {
            if (sendMessage == null || sendMessage.Length == 0)
                throw new Exception("发送信息不能为空");
            if (m_socket.Connected)
            {
                try
                {
                    //暂停接收
                    m_receiveTime.Reset();

                    int flag = m_socket.Send(sendMessage);

                    //等待信号
                    m_receiveTime.WaitOne(timeOut);

                }
                catch (Exception)
                {
                    m_receiveTime.Set();
                    Status = Status.失败;
                }
            }
            return ReceiveByte;
        }
        public string Send(string sendMessage, int timeOut = 700)
        {
            lock (obj)
            {
                ReceiveString = string.Empty;
                if (sendMessage == null || sendMessage.Length == 0)
                    return ReceiveString;
                if (m_socket.Connected)
                {
                    try
                    {
                        //暂停接收
                        m_receiveTime.Reset();

                        byte[] bs = m_encoding.GetBytes(sendMessage);
                        int flag = m_socket.Send(bs);

                        //等待信号
                        m_receiveTime.WaitOne(timeOut);

                    }
                    catch (Exception)
                    {
                        m_receiveTime.Set();
                        Status = Status.失败;
                    }
                }
                return ReceiveString;
            }
        }
        /// <summary>
        /// 同步接收
        /// </summary>
        /// <param name="waitReceiveTime"></param>
        /// <returns></returns>
        public string Receive(int waitReceiveTime = 500)
        {
            string result = string.Empty;
            if (Status == Status.成功)
            {
                try
                {
                    //第一种写法
                    //Stopwatch stopwatch = new Stopwatch();
                    //stopwatch.Start();
                    //while (stopwatch.ElapsedMilliseconds < waitReceiveTime && string.IsNullOrEmpty(ReceiveString))
                    //{
                    //    Task.Delay(100).Wait();
                    //}
                    //stopwatch.Stop();


                    //第二种写法
                    //等待超时
                    m_receiveTime.WaitOne(waitReceiveTime);

                    result = ReceiveString;
                }
                catch (Exception)
                {
                    throw;
                }
            }
            return result;
        }
        private byte[] buffer = new byte[1024];
        private void StartKeepAlive()
        {
            m_socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), m_socket);
        }
        public string ReceiveString { get; private set; }
        public byte[] ReceiveByte { get; private set; }
        private void ReceiveCallBack(IAsyncResult ar)
        {
            try
            {
                Socket socket = ar.AsyncState as Socket;
                if (socket != null)
                {
                    int readByte = socket.EndReceive(ar);
                    if (readByte > 0)
                    {
                        byte[] temp = new byte[readByte];
                        Array.ConstrainedCopy(buffer, 0, temp, 0, readByte);
                        ReceiveString = m_encoding.GetString(temp);

                        ReceiveByte = new byte[readByte];
                        //Array.Copy(temp, ReceiveByte, 0);
                        Array.ConstrainedCopy(temp, 0, ReceiveByte, 0, readByte);
                        m_receiveTime.Set();               //继续
                        ReceiveDataEvent?.Invoke(temp);
                        Console.WriteLine(DateTime.Now + ":收到信息:" + ReceiveString);
                    }
                }
                else
                {
                    Reconnect();
                    //退出，不在beginReceive
                    return;
                }
                m_socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), m_socket);
            }
            catch (Exception ex)
            {
                Log4.Error(ex);
                Status = Status.失败;
                Reconnect();
            }
        }
        private bool IsSocketConnected()
        {
            bool connectStat = true;
            bool blockingState = m_socket.Blocking;
            try
            {
                byte[] tmp = new byte[1];
                m_socket.Blocking = false;
                m_socket.Send(tmp, 0, 0);
                connectStat = true;
            }
            catch (SocketException e)
            {
                if (e.NativeErrorCode.Equals("10035"))
                {
                    connectStat = true;
                }
                else
                {
                    connectStat = false;
                }
            }
            finally
            {
                m_socket.Blocking = blockingState;
            }
            return connectStat;
        }

        public void Start()
        {
            m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            m_socket.SendTimeout = 2000;
            m_socket.ReceiveTimeout = 2000;
            m_connectTime.Reset();
            try
            {
                SetXintiao();
                m_socket.BeginConnect(m_ipEndPoint, new AsyncCallback(ConnectionCallBack), m_socket);
            }
            catch (Exception ex)
            {
                Status = Status.失败;
                Console.WriteLine(ex.Message);
            }
            if (!m_connectTime.WaitOne(2000, false))
            {
                Status = Status.失败;
            }
        }

        public void Stop()
        {
            m_socket?.Close();
            m_socket?.Dispose();
        }
    }
}
